#pragma once

#include <QString>
#include <QIcon>
#include <QByteArray>
#include <QUuid>
#include <QDateTime>
#include <QFile>
#include <QFileInfo>

#include "base.qpb.h"

/********************************
 * 日志
 *******************************/
namespace ns_log
{
    enum class LogLevel {
        Info,
        Debug,
        Warning,
        Error,
        Fatal
    };


    inline QDebug Log(LogLevel level, const QString &file, int line)
    {

        QString levelStr;
        switch(level) {
        case LogLevel::Info:    levelStr = "INFO"; break;
        case LogLevel::Debug:   levelStr = "DEBUG"; break;
        case LogLevel::Warning: levelStr = "WARNING"; break;
        case LogLevel::Error:  levelStr = "ERROR"; break;
        case LogLevel::Fatal:  levelStr = "FATAL"; break;
        }

        QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");

        // 简化文件名显示（只显示最后一部分）
        QFileInfo fileinfo(file);
        QString simplifiedFile = fileinfo.fileName();

        // QString simplifiedFile = file.mid(file.lastIndexOf('\\') + 1);

        //qDebug默认会打印双引号，加上noquote就没有了
        return qDebug().noquote() <<
               QString("[%1][%2:%3][%4]").arg(levelStr,simplifiedFile,QString::number(line),timestamp);
    }
}

// 使用宏定义简化调用
#define LOG_INFO    ns_log::Log(ns_log::LogLevel::Info,    __FILE__, __LINE__)
#define LOG_DEBUG   ns_log::Log(ns_log::LogLevel::Debug,   __FILE__, __LINE__)
#define LOG_WARNING ns_log::Log(ns_log::LogLevel::Warning, __FILE__, __LINE__)
#define LOG_ERROR   ns_log::Log(ns_log::LogLevel::Error,   __FILE__, __LINE__)
#define LOG_FATAL   ns_log::Log(ns_log::LogLevel::Fatal,   __FILE__, __LINE__)

namespace model{

/********************************
 * 工具
 *******************************/
namespace tool{
    // 通过这个方法生成唯一的 messageId
    static inline QString makeId(const QString& type)
    {
        //原本生成的UUID："{52fdaef1-451e-4c01-8a29-90b307f48ab9}"，我们只取后12位，前面再加上是什么类型的uuid
        return type + QUuid::createUuid().toString().sliced(25,12);
    }

    // 通过这个函数得到 秒级 的时间
    static inline int64_t getTime()
    {
        return QDateTime::currentSecsSinceEpoch();
    }

    // 要求函数的定义如果写在 .h 中, 必须加 static 或者 inline (当然两个都加也可以), 避免链接阶段出现 "函数重定义" 的问题.
    static inline QString formatTime(int64_t timestamp)
    {
        // 先把时间戳, 转换成 QDateTime 对象
        QDateTime datetime = QDateTime::fromSecsSinceEpoch(timestamp);
        // 把 QDateTime 对象转成 "格式化时间"
        return datetime.toString("MM-dd HH:mm:ss");
    }

    // 根据 QByteArray, 转成 QIcon
    static inline QIcon makeIcon(const QByteArray& bytearray)
    {
        QPixmap pixmap;
        pixmap.loadFromData(bytearray);
        QIcon icon(pixmap);
        return icon;
    }

    // 读写文件操作.
    // 从指定文件中, 读取所有的二进制内容. 得到一个 QByteArray
    static inline QByteArray loadFileToByteArray(const QString& path)
    {
        QFile file(path);
        bool ok = file.open(QFile::ReadOnly);
        if(!ok)
        {
            LOG_ERROR << "文件打开失败！";
            return QByteArray();
        }
        QByteArray content = file.readAll();
        file.close();
        return content;
    }

    // 把 QByteArray 中的内容, 写入到某个指定文件里
    static inline void writeContentToFile(const QString& path,const QByteArray& content)
    {
        QFile file(path);
        bool ok = file.open(QFile::WriteOnly);
        if(!ok)
        {
            LOG_ERROR << "文件打开失败！";
            return;
        }
        file.write(content);
        file.flush();
        file.close();
    }
}



/********************************
 * 用户信息
 *******************************/

class UserInfo{
public:
    QString userId = "";            // 用户编号
    QString nickname = "";          // 用户昵称
    QString description = "";       // 用户签名
    QString phone = "";             // 手机号码
    QIcon avatar;                   // 用户头像

    // 从protobuf的UserInfo对象，转成当前代码的UserInfo对象
    // 到时服务端发过来的proto。反序列化后得到的对象，传进入就进行录入了
    void load(const pb::UserInfo& userInfo)
    {
        userId = userInfo.userId();
        nickname = userInfo.nickname();
        description = userInfo.description();
        phone = userInfo.phone();
        // 如果头像为空，先用默认头像
        if(userInfo.avatar().isEmpty())
        {
            avatar = QIcon(":/resource/image/defaultAvatar.png");
        }
        else
        {
            avatar = tool::makeIcon(userInfo.avatar());
        }

    }
};

/********************************
 * 消息信息
 *******************************/
enum MessageType
{
    TEXT_TYPE,      // 文本消息
    IMAGE_TYPE,     // 图片消息
    FILE_TYPE,      // 文件消息
    SPEECH_TYPE     // 语音消息
};

class Message
{
public:
    QString messageId = "";                     // 消息的编号
    QString chatSessionId = "";                 // 消息所属会话的编号
    QString time = "";                          // 消息的时间. 通过 "格式化" 时间的方式来表示. 形如 06-07 12:00:00
    UserInfo sender;                            // 发送者的信息
    MessageType messageType = TEXT_TYPE;        // 消息类型
    QByteArray content;                         // 消息的正文内容
    QString fileId = "";                        // 文件的身份标识. 当消息类型为 文件, 图片, 语音 的时候, 才有效. 当消息类型为 文本, 则为 ""
    QString fileName = "";                      // 文件名称. 只是当消息类型为 文件 消息, 才有效. 其他消息均为 ""

     // 此处 extraInfo 目前只是在消息类型为文件消息时, 作为 "文件名" 补充.
    static Message createMeaasge(MessageType type,const QString& chatsessionid,const UserInfo& sender, const QString& content,const QString& extraInfo = "")
    {
        if(type == TEXT_TYPE)
        {
            return makeTextMessage(chatsessionid,sender,content);
        }
        else if(type == IMAGE_TYPE)
        {
            return makeImageMessage(chatsessionid,sender,content);
        }
        else if(type == FILE_TYPE)
        {
            return makeFileMessage(chatsessionid,sender,content,extraInfo);
        }
        else if(type == SPEECH_TYPE)
        {
            return makeSpeechMessage(chatsessionid,sender,content);
        }
        else
        {
            // 触发了未知的消息类型
            return Message();
        }
    }

    // 从pb中录入
    void load(const pb::MessageInfo& messageInfo)
    {
        this->messageId = messageInfo.messageId();
        this->chatSessionId = messageInfo.chatSessionId();
        this->time = tool::formatTime(messageInfo.timestamp());
        this->sender.load(messageInfo.sender());

        // 设置消息类型
        auto type = messageInfo.message().messageType();
        if(type == pb::MessageTypeGadget::MessageType::STRING)
        {
            this->messageType = TEXT_TYPE;
            content = messageInfo.message().stringMessage().content().toUtf8();
        }
        else if(type == pb::MessageTypeGadget::MessageType::IMAGE)
        {
            this->messageType = IMAGE_TYPE;
            if(messageInfo.message().imageMessage().hasImageContent())
            {
                this->content = messageInfo.message().imageMessage().imageContent();
            }
            if(messageInfo.message().imageMessage().hasFileId())
            {
                this->fileId = messageInfo.message().imageMessage().fileId();
            }
        }
        else if(type == pb::MessageTypeGadget::MessageType::SPEECH)
        {
            this->messageType = SPEECH_TYPE;
            if(messageInfo.message().speechMessage().hasFileContents())
            {
                this->content = messageInfo.message().speechMessage().fileContents();
            }
            if(messageInfo.message().speechMessage().hasFileId())
            {
                this->fileId = messageInfo.message().speechMessage().fileId();
            }
        }
        else if(type == pb::MessageTypeGadget::MessageType::FILE)
        {
            this->messageType = FILE_TYPE;
            if(messageInfo.message().fileMessage().hasFileContents())
            {
                this->content = messageInfo.message().fileMessage().fileContents();
            }
            if(messageInfo.message().fileMessage().hasFileId())
            {
                this->fileId = messageInfo.message().fileMessage().fileId();
            }
            this->fileName = messageInfo.message().fileMessage().fileName();
        }
        else
        {
            // 错误的类型
            LOG_DEBUG << "非法的消息类型! type=" << type;
        }
    }

private:

    static Message makeTextMessage(const QString& chatsessionid,const UserInfo& sender,const QString& content)
    {
        Message message;
        // 此处需要确保, 设置的 messageId 是 "唯一" 的
        message.messageId = tool::makeId("M");
        message.chatSessionId = chatsessionid;
        message.time = tool::formatTime(tool::getTime());
        message.messageType = TEXT_TYPE;
        message.content = content.toUtf8();
        message.sender = sender;
        // 对于文本消息来说, 这俩属性不使用, 设为 ""
        message.fileId = "";
        message.fileName = "";
        return message;
    }
    static Message makeImageMessage(const QString& chatsessionid,const UserInfo& sender,const QString& content)
    {
        Message message;
        // 此处需要确保, 设置的 messageId 是 "唯一" 的
        message.messageId = tool::makeId("M");
        message.chatSessionId = chatsessionid;
        message.time = tool::formatTime(tool::getTime());
        message.messageType = IMAGE_TYPE;
        message.sender = sender;
        // fileId 后续使用的时候再进一步设置
        message.fileId = "";
        // fileName 不使用, 直接设为 ""
        message.fileName = "";
        return message;
    }
    static Message makeFileMessage(const QString& chatsessionid,const UserInfo& sender,const QString& content,const QString& filename)
    {
        Message message;
        // 此处需要确保, 设置的 messageId 是 "唯一" 的
        message.messageId = tool::makeId("M");
        message.chatSessionId = chatsessionid;
        message.time = tool::formatTime(tool::getTime());
        message.messageType = FILE_TYPE;
        message.sender = sender;
        // fileId 后续使用的时候再进一步设置
        message.fileId = "";
        message.fileName = filename;
        return message;
    }
    static Message makeSpeechMessage(const QString& chatsessionid,const UserInfo& sender,const QString& content)
    {
        Message message;
        // 此处需要确保, 设置的 messageId 是 "唯一" 的
        message.messageId = tool::makeId("M");
        message.chatSessionId = chatsessionid;
        message.time = tool::formatTime(tool::getTime());
        message.messageType = SPEECH_TYPE;
        message.sender = sender;
        // fileId 后续使用的时候再进一步设置
        message.fileId = "";
        // fileName 不使用, 直接设为 ""
        message.fileName = "";
        return message;
    }
};

/********************************
 * 会话信息
 *******************************/

class ChatSessionInfo
{
public:
    QString chatsessionId = "";     // 会话编号
    QString chatsessionName = "";   // 会话名字, 如果会话是单聊, 名字就是对方的昵称; 如果是群聊, 名字就是群聊的名称.
    Message lastMessage;            // 表示最新的消息
    QIcon avatar;                   // 会话头像. 如果会话是单聊, 头像就是对方的头像; 如果是群聊, 头像就是群聊的头像.
    QString userId = "";            // 对于单聊来说, 表示对方的用户 id, 对于群聊设为 ""

    //录入接口
    void load(const pb::ChatSessionInfo& chatSessionInfo)
    {
        this->chatsessionId = chatSessionInfo.chatSessionId();
        this->chatsessionName = chatSessionInfo.chatSessionName();
        if(chatSessionInfo.hasSingleChatFriendId())
        {
            this->userId = chatSessionInfo.singleChatFriendId();
        }
        if(chatSessionInfo.hasPrevMessage())
        {
            this->lastMessage.load(chatSessionInfo.prevMessage());
        }
        if(chatSessionInfo.hasAvatar())
        {
            // 已经有头像
            this->avatar = tool::makeIcon(chatSessionInfo.avatar());
        }
        else
        {
            // 如果没有头像, 则根据当前会话是单聊还是群聊, 使用不同的默认头像.
            if(userId != "")
            {
                // 单聊
                this->avatar = QIcon(":/resource/image/defaultAvatar.png");
            }
            else
            {
                // 群聊
                this->avatar = QIcon(":/resource/image/groupAvatar.png");
            }
        }
    }
};
}   //end namespace medel
